#include <Windows.h>
#include <gl\GL.h>
#include <gl\GLU.h>
#include <gl\glut.h>
#include <math.h>

using namespace std;

#include "gameobj.h"

#define PI 3.1415

/*
void gameobj::draw()
{
	cout << "shouldn't be here" << endl;
}
void gameobj::update(float deltatime)
{
}*/

tile::tile()
{
	objType = TILE;

	material[0] = 0.2;
	material[1] = 1;
	material[2] = 0.2;
	material[3] = 1;
}


void tile::draw()
{
	glEnable(GL_LIGHTING);

	list<vec3>::const_iterator itor = this->coordinates.begin();
	list<vec3>::const_iterator end = this->coordinates.end();

	//cout << "Tile ID: " << tileID << endl;
	//cout << "NVerts: " << nVerts << endl;
	//cout << "RealVerts: " << coordinates.size() << endl;
	
	//glColor3f(0, 1, 0);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material);
	glNormal3f(normal.vx, normal.vy, normal.vz);

	if(pointIn(worldPtr->ballLoc->location))
	{
		glDisable(GL_LIGHTING);
		glColor3f(1, 0, 0);
	}

	glBegin(GL_POLYGON);	



	for(; itor != end; itor++)
	{

		//cout << *itor << endl;
		glVertex3f(itor->vx, itor->vy, itor->vz);
	}
	glEnd();

	//drawLines();
	//normalLines();
	



}

void tile::normalLines()
{
	list<vec3>::const_iterator itor = this->coordinates.begin();
	list<vec3>::const_iterator end = this->coordinates.end();

	for(;itor != end; itor++)
	{
		glColor3f(0.8,0.8,1);
		glBegin(GL_LINES);		//drawing normals
			vec3 draw = *itor;
			glVertex3f(draw.vx, draw.vy, draw.vz);
			draw = draw + (normal / 2);
			glVertex3f(draw.vx, draw.vy, draw.vz);
		
		glEnd();
	}
}

void tile::drawLines()	//just to draw borderlines, makes it less confusing.
{
	glDisable(GL_LIGHTING);

	list<vec3>::const_iterator itor = this->coordinates.begin();
	list<vec3>::const_iterator itor2 = this->coordinates.begin();
	itor2++;
	list<vec3>::const_iterator end = this->coordinates.end();

	list<int>::const_iterator nitor = neighbors.begin();
	list<int>::const_iterator nend = neighbors.end();

	while(nitor != nend)
	{
		if(itor2 == end)
		{
			itor2 = this->coordinates.begin();	//set to begin
		}

		vec3 first;
		vec3 second;

		if(*nitor == 0)
		{
			first.vx = itor->vx;
			first.vy = itor->vy;
			first.vz = itor->vz;

			second.vx = itor2->vx;
			second.vy = itor2->vy;
			second.vz = itor2->vz;
		}

		itor++;
		itor2++;
		nitor++;

		glColor3f(1,0,0);
		glLineWidth(2);
		glBegin(GL_LINES);
			glVertex3f(first.vx, first.vy, first.vz);
			glVertex3f(second.vx, second.vy, second.vz);
			
		glEnd();
	}

}


void tile::update(float deltatime)
{
	if(pointIn(worldPtr->ballLoc->location))
		worldPtr->ballLoc->currTile = this;
}

ball::ball()
{
	radius = 0.05;
	//worldPtr->ballLoc = new vec3(0,0,0);
	objType = ENTITY;
	velocity = 0.1;
	direction = vec3(1,0,-1);
}

void ball::draw()
{
	glDisable(GL_LIGHTING);

	glPushMatrix();
	glColor3f(1,1,1);
	glTranslatef(location.vx, location.vy+radius, location.vz);

	glutSolidSphere(radius, 20, 20);

	glColor3f(1,0,0);
	glPopMatrix();
}

void ball::update(float deltatime)
{
	vec3 NormDir = direction.normalize();

	tile* curTile = this->currTile;
	vec3 normal = curTile->getNormal();
	vec3 up(0,1,0);
	vec3 xVec = up.cross(normal);
	vec3 rVec = xVec.cross(normal);//for gravity falling

	vec3 xzVec(direction.vx, 0, direction.vz);
	xVec = up.cross(xzVec);
	vec3 dVec = xVec.cross(normal);
	NormDir = dVec.normalize();
	


	//location.vx += 0.005f * deltatime;
	//location.vz -= 0.01f * deltatime;

		location = location + (NormDir)/1000;
	direction = direction + (rVec.normalize()/500);

	worldPtr->ballLoc = this;
	
}

void ball::interact()
{

}

void ball::bounce(vec3 norm)
{
	float angle = vec3(direction.vx, 0.0f, direction.vz).dot(vec3(norm.vx, 0.0f, norm.vz));
	cout << norm << endl;
	vec3 projection = norm * (angle/norm.length());
	projection = projection * -1;
	direction = projection + (projection + direction);
}

wall::wall(vec3 c1, vec3 c2)
{
	objType = WALL;
	coordinates.push_back(c1);
	coordinates.push_back(c2);
	normal = vec3(0,1,0).cross((c1 - c2));
	normal = normal.normalize();
	D = -1*(normal.vx * c1.vx) - (normal.vy * c1.vy) - (normal.vz * c1.vz);
	bounce = false;
	midPoint = vec3((c1.vx + c2.vx)/2,(c1.vy + c2.vy)/2,(c1.vz + c2.vz)/2);
	length = c1.distance(c2);

}

float wall::distance(vec3 point)
{
	return (abs( (normal.vx*point.vx) + (normal.vy*point.vy) + (normal.vz*point.vz) + D)/
		sqrt((normal.vx*normal.vx)+(normal.vy*normal.vy)+(normal.vz*normal.vz)));
}

void wall::update(float deltaTime)
{
	vec3 ballLocation = worldPtr->ballLoc->location;
	if(distance(ballLocation) < worldPtr->ballLoc->radius && !bounce && midPoint.distance(ballLocation) < length/2 + worldPtr->ballLoc->radius)
	{
		bounce = true;
		worldPtr->ballLoc->bounce(normal);//bounces the ball off the wall
	}
	else if(distance(ballLocation) > worldPtr->ballLoc->radius && bounce){
		bounce = false;
	}
}

void wall::draw()
{
	glDisable(GL_LIGHTING);

	glColor3f(1,0,0);
	glLineWidth(3);

	list<vec3>::const_iterator itor = coordinates.begin();
	list<vec3>::const_iterator end = coordinates.end();

	glBegin(GL_LINES);
	for(; itor != end ; itor++)
	{
		glVertex3f(itor->vx, itor->vy, itor->vz);
	}
	glEnd();
}

cup::cup()
{
	objType = CUP;
}

void cup::draw()
{
	glDisable(GL_LIGHTING);

	float radius = 0.10;
	glPushMatrix();
	glColor3f(0,0,0);
	
	glTranslatef(location.vx, location.vy, location.vz);

	glPushMatrix();
	glScalef(1, 0.25, 1);
	glutSolidSphere(radius, 20, 20);
	glPopMatrix();

	drawPole();
	//glutWired(10, 20, 20);
	//glutWireSphere(10, 20,20);
	glPopMatrix();
}

void cup::drawPole()
{
	glPushMatrix();
	glScalef(0.04, 0.15, 0.04);
	glTranslatef(0, 0.5, 0);
	for(int i = 0; i < 3; i++)
	{
	glColor3f(1,0,0);
	glutSolidCube(1);
	glTranslatef(0, 1, 0);
	glColor3f(1,1,0);
	glutSolidCube(1);
	glTranslatef(0, 1, 0);
	}
	glPopMatrix();
}

void cup::update(float deltatime)
{
	//check ball distance away from radius
}

gameWorld::~gameWorld()
{

}

vec3 tile::getNormal()
{
	list<vec3>::const_iterator a = coordinates.begin();
	list<vec3>::const_iterator b = coordinates.begin();
	b++;
	list<vec3>::const_iterator c = coordinates.begin();
	c++; c++;

	list<vec3>::const_iterator end = coordinates.end();

	vec3 normal(0,0,0);


		vec3 v1, v2;

		v1 = *a - *b;
		v2 = *a - *c;

	return v1.cross(v2).normalize();
	

	//return normal.normalize();
}

void tile::makeWall(list<gameobj*> &toAdd)
{

	list<vec3>::const_iterator itor = this->coordinates.begin();
	list<vec3>::const_iterator itor2 = this->coordinates.begin();
	itor2++;
	list<vec3>::const_iterator end = this->coordinates.end();

	list<int>::const_iterator nitor = neighbors.begin();
	list<int>::const_iterator nend = neighbors.end();

	while(nitor != nend)
	{
		if(itor2 == end)
		{
			itor2 = this->coordinates.begin();	//set to begin
		}

		vec3 first;
		vec3 second;

		if(*nitor == 0)
		{
			first.vx = itor->vx;
			first.vy = itor->vy;
			first.vz = itor->vz;

			second.vx = itor2->vx;
			second.vy = itor2->vy;
			second.vz = itor2->vz;
		}

		itor++;
		itor2++;
		nitor++;
			
		toAdd.push_back(new wall(first, second));
			
	}
}

float angle2D(float x1, float y1, float x2, float y2)
{
	float dtheta, theta1, theta2;
	theta1 = atan2(y1, x1);
	theta2 = atan2(y2, x2);

	dtheta = theta2 - theta1;
	while(dtheta > PI)
	{
		dtheta -= 2 * PI;
	}
	while( dtheta < -PI)
	{
		dtheta += 2 * PI;
	}

	return dtheta;

}

bool tile::pointIn(vec3 point)
{
	list<vec3>::iterator itor = this->coordinates.begin();
	list<vec3>::iterator itor2 = this->coordinates.begin();
	itor2++;
	list<vec3>::const_iterator end = coordinates.end();
	vec3 p1, p2;	//x and z dont worry about y

	float angle = 0;
	//.h is x 
	//.v is z
	for(; itor != end; itor++)
	{
		if(itor2 == end)
			itor2 = coordinates.begin();

		p1.vx = itor->vx - point.vx;
		p1.vz = itor->vz - point.vz;
		p2.vx = itor2->vx - point.vx;
		p2.vz = itor2->vz - point.vz;

		angle += angle2D(p1.vx, p1.vz, p2.vx, p2.vz);

		itor2++;
	}

	if(abs(angle) < PI)
		return false;

	else
		return true;
}

void gameWorld::createLevel(char* fileName)
{
	ifstream fil;
	fil.open(fileName);

	level tileList;
	


	while(fil.good())
	{
		gameobj *newObj;
		string line;
		getline(fil, line);

		wordvec words;
		words = split(line, " \t");

		if(words.size() > 0)
		{
			int newID;
			int n;

			if(!words.at(0).compare("tile"))
			{
				tile *newTile = new tile();
				n = atoi(words.at(2).c_str());	//get size;

				for(int i = 0; i < n; i++)
				{
					int index = i + 1;
					index *= 3;

					if(index+2 > words.size()) 
					{
						cout << "I/O File Error" << endl; 
						break;
					}


					float pX, pY, pZ;
					pX = atof(words.at(index).c_str());
					pY = atof(words.at(index+1).c_str());
					pZ = atof(words.at(index+2).c_str());

					vec3 coord(pX, pY, pZ);
					newTile->coordinates.push_back(coord);
				}
				for(int i = words.size() - n; i < words.size(); i++)
				{
					newTile->neighbors.push_back(atoi(words.at(i).c_str()));
				}

				newTile->nVerts = n;
				newTile->tileID = atoi(words.at(1).c_str());
				newTile->objType = TILE;
				newTile->normal = newTile->getNormal();
				newTile->makeWall(tileList);	
				newObj = newTile;

				list<vec3>::const_iterator cItor = newTile->coordinates.begin();
				list<vec3>::const_iterator cEnd = newTile->coordinates.end();
				for(; cItor != cEnd; cItor++)
				{
					vec3 print = *cItor;
					//cout << print  << endl;
				}
			}

			if(!words.at(0).compare("tee"))
			{
				ball* start = new ball();
				float pX, pY, pZ;
				pX = atof(words.at(2).c_str());
				pY = atof(words.at(3).c_str());
				pZ = atof(words.at(4).c_str());

				vec3 newVec(pX, pY, pZ);
				start->location = newVec;
				newObj = start;
				this->ballLoc = start;
			}
			if(!words.at(0).compare("cup"))
			{
				cup* ballEnd = new cup();
				float pX, pY, pZ;
				pX = atof(words.at(2).c_str());
				pY = atof(words.at(3).c_str());
				pZ = atof(words.at(4).c_str());

				vec3 newVec(pX, pY, pZ);
				ballEnd->location = newVec;
				newObj = ballEnd;
			}
			
			tileList.push_back(newObj);
		}
	}

	this->mLevelList.insert(pair<string, level>(fileName, tileList));
}

void gameWorld::draw()
{
	list<gameobj*>::iterator itor = mObjList.begin();
	list<gameobj*>::iterator end = mObjList.end();

	for(;itor != end; itor++)
	{
		(*itor)->draw();
	}

}

void gameWorld::update(float deltaTime)
{
	list<gameobj*>::iterator itor = mObjList.begin();
	list<gameobj*>::iterator end = mObjList.end();

	for(;itor != end; itor++)
	{
		(*itor)->update(deltaTime);
	}

	itor = mAddList.begin();
	end = mAddList.end();
	for(;itor != end; itor++)
	{
		(*itor)->worldPtr = this;
		mObjList.push_back(*itor);
	}

	mAddList.clear();

	itor = mRemoveList.begin();
	end = mRemoveList.end();
	for(;itor != end; itor++)
	{
		mObjList.remove(*itor);
	}

	mRemoveList.clear();

}

void gameWorld::loadLevel(string fileName)
{
	level temp = mLevelList[fileName];

	this->mObjList.clear();	//clear obj list
	this->mAddList.clear();
	this->mRemoveList.clear();
	//this->mCurrLevel.clear();

	level::const_iterator itor = temp.begin();
	level::const_iterator end = temp.end();

	for(; itor != end; itor++)
	{
		gameobj* tempObj = *itor;
		this->mAddList.push_back(tempObj);
	}
}

void gameWorld::printObjList()
{
	list<gameobj*>::const_iterator itor = mObjList.begin();
	list<gameobj*>::const_iterator end = mObjList.end();

	int n = 0;
	for(; itor != end; itor++)
	{
		cout << "List: " << n << endl;
		n++;
	}
}

void gameWorld::camera(float &distance, const float Yangle, const float Xangle,  vec3 lookAt)
{
	vec3 camPos(0,0,0);
	float trad = Yangle * PI/180;	//theta
	float prad = Xangle * PI/180;	//phi

	if(distance < 1)
	{
		distance = 1;
	}

	camPos.vx = -distance * cos(trad) * cos(prad);
	camPos.vy = distance * sin(trad);
	camPos.vz = distance * cos(trad) * sin(prad);

	/*	camPos.vx += lookAt.vx;
	camPos.vy += lookAt.vy;
	camPos.vz += lookAt.vz;*/
	camPos = camPos + lookAt;
	/*
	glPushMatrix();
		glLoadIdentity();
		glTranslatef(lookAt.vx, lookAt.vy, lookAt.vz);
		glutWireSphere(distance, 20, 20);
	
	glPopMatrix();
	*/
	glLoadIdentity();
	gluLookAt(	camPos.vx, camPos.vy, camPos.vz,
				lookAt.vx, lookAt.vy, lookAt.vz,
				0, 1, 0);
	
	//cout << "CAM RUNNING" << endl;

	//cout << "Look At Vec: " << lookAt << endl;
	//cout << "Cam Vec: " << camPos << endl;
	//cout << "Cam Distance: " << camPos.distance(lookAt) << endl;
}